//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-06-14     ZhangYihua   first version
//
// Description  : transferring pulse between two clock domains with lower rate. The
//                min gap between two transmission is about (SYNC_NUM_D2S+1) clk_src
//                + (SYNC_NUM_S2D+1) clk_dst clock steps
//################################################################################

module sync_pls #(
parameter           CNT_BW                  = 0,    // the max number of uncompleted pulse is 2^CNT_BW
parameter           SYNC_NUM_D2S            = 3,    // min gap between two transmission about SYNC_NUM_D2S+SYNC_NUM_S2D+2 clock steps
parameter           SYNC_NUM_S2D            = 3
) ( 
input                                       rst_src_n,
input                                       clk_src,

input                                       src_pls,    // keep src_pls 1'b1 when src_rdy 1'b0 unless missing pulse make no difference to system
output                                      src_rdy,    // ignore src_rdy and set MISS_PLS_CHKEN=0 if not care missing pulse

input                                       rst_dst_n,
input                                       clk_dst,

output                                      dst_pls,
input                                       dst_rdy
);

//################################################################################
// define local varialbe and localparam
//################################################################################
wire                                        dst_tog_d2s;
wire                                        src_busy;
wire                                        src_done;
reg                                         src_tog;
wire                                        src_nfull;
wire                                        src_nempty;
wire                                        src_tog_s2d;
wire                                        dst_done;
reg                                         dst_tog;

//################################################################################
// main
//################################################################################

assign src_rdy  = (~src_busy) | src_nfull;

sync_dff #(
        .SYNC_NUM                       (SYNC_NUM_D2S                   ),
        .BW                             (1                              ),
        .INI                            (1'b0                           )
) u_tog_d2s ( 
        .rst_n                          (rst_src_n                      ),
        .clk                            (clk_src                        ),

        .d                              (dst_tog                        ),
        .q                              (dst_tog_d2s                    )
);

assign src_busy = src_tog ^ dst_tog_d2s;
assign src_done = (~src_busy) & (src_pls | src_nempty);
always@(posedge clk_src or negedge rst_src_n) begin
    if (rst_src_n==1'b0) begin
        src_tog <=`U_DLY 1'b0;
    end else begin
        if (src_done==1'b1)
            src_tog <=`U_DLY ~src_tog;
        else
            ;
    end
end

generate if (CNT_BW==0) begin:G_NCNT

    assign src_nfull  = 1'b0;
    assign src_nempty = 1'b0;
    
end else begin:G_CNT

    wire                                        inc;
    wire                                        dec;
    reg                 [CNT_BW-1:0]            src_cnt;

    assign inc =   src_pls  &   src_busy  & src_nfull;
    assign dec = (~src_pls) & (~src_busy) & src_nempty;
    always@(posedge clk_src or negedge rst_src_n) begin
        if (rst_src_n==1'b0) begin
            src_cnt <=`U_DLY {CNT_BW{1'b0}};
        end else begin
            case ({dec, inc})
                2'b01   : src_cnt <=`U_DLY src_cnt + 1'd1;
                2'b10   : src_cnt <=`U_DLY src_cnt - 1'd1;
                default : src_cnt <=`U_DLY src_cnt;
            endcase
        end
    end
    assign src_nfull  = (src_cnt<{CNT_BW{1'b1}}) ? 1'b1 : 1'b0;
    assign src_nempty = (src_cnt>{CNT_BW{1'b0}}) ? 1'b1 : 1'b0;
    
end endgenerate

// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------

sync_dff #(
        .SYNC_NUM                       (SYNC_NUM_S2D                   ),
        .BW                             (1                              ),
        .INI                            (1'b0                           )
) u_tog_s2d ( 
        .rst_n                          (rst_dst_n                      ),
        .clk                            (clk_dst                        ),

        .d                              (src_tog                        ),
        .q                              (src_tog_s2d                    )
);

assign dst_pls  = dst_tog ^ src_tog_s2d; 
assign dst_done = dst_pls & dst_rdy;
always@(posedge clk_dst or negedge rst_dst_n) begin
    if (rst_dst_n==1'b0) begin
        dst_tog <=`U_DLY 1'b0;
    end else begin
        if (dst_done==1'b1)
            dst_tog <=`U_DLY ~dst_tog;
        else
            ;
    end
end

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off

reg     MISS_PLS_CHKEN;

a_miss_pls: assert property (@(posedge clk_src) disable iff (!rst_src_n)
    ((MISS_PLS_CHKEN!==1'b0)&($fell(src_pls)) |-> $past(src_rdy))
) else begin
    $error("miss a pulse of src_pls.");
end

// synopsys translate_on
`endif

endmodule
